home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
language
/
pcl_src.zoo
/
cache.lsp
< prev
next >
Wrap
Text File
|
1992-09-09
|
60KB
|
1,601 lines
;;;-*-Mode:LISP; Package:(PCL LISP 1000); Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
;;; All rights reserved.
;;;
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted. Any distribution of this
;;; software or derivative works must comply with all applicable United
;;; States export control laws.
;;;
;;; This software is made available AS IS, and Xerox Corporation makes no
;;; warranty about the software, its performance or its conformity to any
;;; specification.
;;;
;;; Any person obtaining a copy of this software is requested to send their
;;; name and post office or electronic mail address to:
;;; CommonLoops Coordinator
;;; Xerox PARC
;;; 3333 Coyote Hill Rd.
;;; Palo Alto, CA 94304
;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
;;;
;;; Suggestions, comments and requests for improvements are also welcome.
;;; *************************************************************************
;;;
;;; The basics of the PCL wrapper cache mechanism.
;;;
(in-package 'pcl)
;;;
;;; The caching algorithm implemented:
;;;
;;; << put a paper here >>
;;;
;;; For now, understand that as far as most of this code goes, a cache has
;;; two important properties. The first is the number of wrappers used as
;;; keys in each cache line. Throughout this code, this value is always
;;; called NKEYS. The second is whether or not the cache lines of a cache
;;; store a value. Throughout this code, this always called VALUEP.
;;;
;;; Depending on these values, there are three kinds of caches.
;;;
;;; NKEYS = 1, VALUEP = NIL
;;;
;;; In this kind of cache, each line is 1 word long. No cache locking is
;;; needed since all read's in the cache are a single value. Nevertheless
;;; line 0 (location 0) is reserved, to ensure that invalid wrappers will
;;; not get a first probe hit.
;;;
;;; To keep the code simpler, a cache lock count does appear in location 0
;;; of these caches, that count is incremented whenever data is written to
;;; the cache. But, the actual lookup code (see make-dlap) doesn't need to
;;; do locking when reading the cache.
;;;
;;;
;;; NKEYS = 1, VALUEP = T
;;;
;;; In this kind of cache, each line is 2 words long. Cache locking must
;;; be done to ensure the synchronization of cache reads. Line 0 of the
;;; cache (location 0) is reserved for the cache lock count. Location 1
;;; of the cache is unused (in effect wasted).
;;;
;;; NKEYS > 1
;;;
;;; In this kind of cache, the 0 word of the cache holds the lock count.
;;; The 1 word of the cache is line 0. Line 0 of these caches is not
;;; reserved.
;;;
;;; This is done because in this sort of cache, the overhead of doing the
;;; cache probe is high enough that the 1+ required to offset the location
;;; is not a significant cost. In addition, because of the larger line
;;; sizes, the space that would be wasted by reserving line 0 to hold the
;;; lock count is more significant.
;;;
(declaim (ftype (function () index)
get-wrapper-cache-number))
(declaim (ftype (function (T T T) (values index index index index))
compute-cache-parameters))
(declaim (ftype (function (T T T) index)
compute-primary-cache-location
compute-primary-cache-location-from-location))
(declaim (ftype (function (T) index)
cache-count))
(declaim (ftype (function (T T T T) boolean)
fill-cache-p
fill-cache-from-cache-p))
(declaim (ftype (function (T T &optional T) (values T boolean))
find-free-cache-line))
(declaim (ftype (function (index) index)
compute-line-size
default-limit-fn
power-of-two-ceiling))
(declaim (ftype (function (T) boolean)
free-cache-vector))
;;;
;;; Caches
;;;
;;; A cache is essentially just a vector. The use of the individual `words'
;;; in the vector depends on particular properties of the cache as described
;;; above.
;;;
;;; This defines an abstraction for caches in terms of their most obvious
;;; implementation as simple vectors. But, please notice that part of the
;;; implementation of this abstraction, is the function lap-out-cache-ref.
;;; This means that most port-specific modifications to the implementation
;;; of caches will require corresponding port-specific modifications to the
;;; lap code assembler.
;;;
(defmacro cache-vector-ref (cache-vector location)
`(svref (the simple-vector ,cache-vector)
(#-cmu the #+cmu ext:truly-the fixnum ,location)))
(defun emit-cache-vector-ref (cache-vector-operand location-operand)
(operand :iref cache-vector-operand location-operand))
(defmacro cache-vector-size (cache-vector)
`(array-dimension (the simple-vector ,cache-vector) 0))
(defun allocate-cache-vector (size)
(declare (type index size))
(make-array size :adjustable nil))
(defmacro cache-vector-lock-count (cache-vector)
`(cache-vector-ref ,cache-vector 0))
(defun flush-cache-vector-internal (cache-vector)
(without-interrupts-simple
(fill (the simple-vector cache-vector) nil)
(setf (cache-vector-lock-count cache-vector) 0))
cache-vector)
(defmacro modify-cache (cache-vector &body body)
`(without-interrupts-simple
(multiple-value-prog1
(progn ,@body)
(let ((old-count (cache-vector-lock-count ,cache-vector)))
(declare (type index old-count))
(setf (cache-vector-lock-count ,cache-vector)
(the index
(if (= old-count most-positive-fixnum)
1
(the index (1+ old-count)))))))))
(deftype field-type ()
'(integer 0 ;#.(position 'number wrapper-layout)
7)) ;#.(position 'number wrapper-layout :from-end t)
(eval-when (compile load eval)
(defun power-of-two-ceiling (x)
(declare (type index x))
;;(expt 2 (ceiling (log x 2)))
(the index (ash 1 (integer-length (1- x)))))
(defconstant *nkeys-limit* 256)
)
(defstruct (cache
(:print-function print-cache)
(:constructor make-cache ())
(:copier copy-cache-internal))
(nkeys 1 :type (integer 1 #.*nkeys-limit*))
(valuep nil :type boolean)
(nlines 0 :type index)
(field 0 :type field-type)
(limit-fn #'default-limit-fn :type real-function)
(mask 0 :type index)
(size 0 :type index)
(line-size 1 :type (integer 1 #.(power-of-two-ceiling (1+ *nkeys-limit*))))
(max-location 0 :type index)
(vector '#() :type simple-vector)
(overflow nil :type list))
(defun print-cache (cache stream depth)
(declare (ignore depth))
(printing-random-thing (cache stream)
(format stream "cache ~D ~S ~D"
(cache-nkeys cache) (cache-valuep cache) (cache-nlines cache))))
#+akcl
(si::freeze-defstruct 'cache)
(defmacro cache-lock-count (cache)
`(cache-vector-lock-count (cache-vector ,cache)))
;;;
;;; Some facilities for allocation and freeing caches as they are needed.
;;; This is done on the assumption that a better port of PCL will arrange
;;; to cons these all the same static area. Given that, the fact that
;;; PCL tries to reuse them should be a win.
;;;
(defvar *free-cache-vectors* (make-hash-table :size 16 :test 'eql))
;;;
;;; Return a cache that has had flush-cache-internal called on it. This
;;; returns a cache of exactly the size requested, it won't ever return a
;;; larger cache.
;;;
(defun get-cache-vector (size)
(let ((entry (gethash size *free-cache-vectors*)))
(without-interrupts-simple
(cond ((null entry)
(setf (gethash size *free-cache-vectors*) (cons 0 nil))
(get-cache-vector size))
((null (cdr entry))
(setf (car entry) (the fixnum (1+ (the fixnum (car entry)))))
(flush-cache-vector-internal (allocate-cache-vector size)))
(t
(let ((cache (cdr entry)))
(setf (cdr entry) (cache-vector-ref cache 0))
(flush-cache-vector-internal cache)))))))
(defun free-cache-vector (cache-vector)
(let ((entry (gethash (cache-vector-size cac